#include <signal.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/prctl.h>

#include "psd_conn.h"
#include "psd_logger.h"
#include "psd_config.h"
#include "psd_tpool.h"
#include "psd_path_handler.h"
#include "psd_backend_handler.h"
#include "psd_route.h"

#include "psd_auth.h"
#include "psd_event.h"
#include "psd_protocols.h"
#include "psd_optimize.h"
#include "psd_default_settings.h"
#include "psd_user_settings.h"
#include "psd_route_settings.h"
#include "psd_frontend_default.h"
#include "psd_modules.h"

#include "compat.h"

void sig_exit(int signal) {
	exit(0);
}

int main(int argc, char *argv[]) {
	int c;
	extern char *optarg;
	static char usage[] = "usage: psd [-V] [-B] [-c config_file] [-d debug_level]\n";
	int do_background = 0;
	int debug_level = -1;
	int log_syslog = 0;
	char *logger_file = NULL;
	char *pid_file = NULL;
	char *user = NULL;
	char *group = NULL;
	uid_t uid;
	gid_t gid;
	struct group *gr;
	struct passwd *pw;
#ifdef CONFIG_FILE
	char *conf_file = CONFIG_FILE;
#else
	char *conf_file = NULL;
#endif
        char *modules_dir = NULL;

#ifdef NETLOGGER
	for (c=0; c<MAX_ID; c++)
	  stream_ids[c] = 0;
	
	/* open NetLogger handle, nllog is global */ 
	nllog = NL_open(NULL);
	if (!nllog) {
	  fprintf(stderr, "NETLOGGER: error opening log file\n");
	  exit(-1);
	}
	NL_set_level(nllog, NL_LVL_UDEF);
	
	prog_summ = NL_summ();
	NL_summ_set_shared_output(prog_summ, nllog);
	NL_transfer_init(prog_summ, -1, NL_LVL_UDEF);
	
	if (getenv("NL_LOG_SUMMARY") == NULL)
	  NL_transfer_set_passthrough(prog_summ);
        NL_summ_add_log(prog_summ, nllog);
	
        //int_summ = NL_summ();
        //NL_summ_set_shared_output(int_summ, nllog);
        //NL_transfer_init(int_summ, 1000000, NL_LVL_DEBUG);
        //NL_transfer_set_passthrough(int_summ);
        //NL_summ_add_log(int_summ, nllog);
#endif

	signal(SIGUSR1, sig_exit);
	signal(SIGPIPE, SIG_IGN); 

	while((c = getopt(argc, argv, "Bsd:c:Vo:p:U:G:l:")) != -1) {
		switch(c) {
			case 'U':
				user = strdup(optarg);
				break;

			case 'G':
				group = strdup(optarg);
				break;

			case 'p':
				pid_file = strdup(optarg);
				break;

			case 'B':
				do_background = 1;
				break;

		        case 's':
			        log_syslog = 1;
			        break;

			case 'd':
				debug_level = atoi(optarg);
				break;

			case 'c':
				conf_file = strdup(optarg);
				break;

			case 'o':
				logger_file = strdup(optarg);
				break;

			case 'l':
				modules_dir = strdup(optarg);
				break;

			case 'V':
				printf("Logistical Session Daemon, version 0.4.5\n");
				exit(1);
				break;

			default:
				fprintf(stderr, usage); 
				exit(1);
		}
	}

	if (xsp_init()) {
		fprintf(stderr, "couldn't initialize xsp protocol handler");	    
		goto error_exit;
	}

	if (psd_depot_settings_init()) {
		fprintf(stderr, "couldn't initialize default settings");
		goto error_exit;
	}


	if (psd_default_settings_init()) {
		fprintf(stderr, "couldn't initialize default settings");
		goto error_exit;
	}

	if (psd_user_settings_init()) {
		fprintf(stderr, "couldn't initialize user settings system");
		goto error_exit;
	}

	if (psd_route_settings_init()) {
		fprintf(stderr, "couldn't initialize default settings");
		goto error_exit;
	}

	if (psd_config_read(conf_file)) {
		fprintf(stderr, "reading configuration file failed");
		goto error_exit;
	}


	if (log_syslog) {
	        // the user specified syslog logging via the command line
	        // overrides other logging options
	        psd_depot_settings_set("logger", "backend", "syslog");
	}
	else if (logger_file) {
		// the user specified that the logging should go to this file
		// via the command line
		psd_depot_settings_set("logger", "backend", "file");
		psd_depot_settings_set("logger", "output_file", logger_file);
	}

	if(do_background) {
	        daemonize();
	}


    chdir("/tmp");

	if (!pid_file) {
		if (psd_depot_settings_get_1("pid_file", &pid_file) != 0) {
			pid_file = NULL;
		}
	}

	if (pid_file) {
		FILE *pid_out = fopen(pid_file, "w+");
		if (!pid_out) {
                        printf("Couldn't open pid file: %s\n", pid_file);
			exit(-1);
		}

		fprintf(pid_out, "%d", getpid());
		fclose(pid_out);
	}

	if (!user) {
		if (psd_depot_settings_get_1("user", &user) != 0) {
			user = NULL;
		}
	}

	if (!group) {
		if (psd_depot_settings_get_1("group", &group) != 0) {
			group = NULL;
		}
	}

	if (group) {
		gr = getgrnam(group);
		if (gr) {
			gid = gr->gr_gid;
		}

		if (!gid) {
			psd_err(0, "Invalid group '%s'\n", group);
			exit(-1);
		}

		if (setgid(gid) < 0) {
			psd_err(0, "Couldn't change process group to %s", group);
		}
	}

	if (user) {
		pw = getpwnam(user);
		if (pw) {
			uid = pw->pw_uid;
		}

		if (!uid) {
			psd_err(0, "Invalid user '%s'\n", user);
			exit(-1);
		}

		if (setuid(uid) < 0) {
			psd_err(0, "Couldn't change process user to %s", user);
		}
	}

	// ensure that we can generate a core dump even if we changed uids
	prctl(PR_SET_DUMPABLE, 1);

	if (psd_tpool_init()) {
		psd_err(0, "couldn't initialize thread pooling system");
		goto error_exit;
	}

	if (psd_modules_init(modules_dir) != 0) {
		psd_err(0, "couldn't initialize moduler loader");
		goto error_exit;
	}

	if (psd_logger_init(debug_level)) {
		psd_err(0, "couldn't initialize the event log");
		goto error_exit;
	}

	if (psd_sessions_init()) {
		psd_err(0, "couldn't initialize session system");
		goto error_exit;
	}

	if (psd_backend_handler_init()) {
                psd_err(0, "couldn't initialize transfer backend handler system");
                goto error_exit;
        }

	if (psd_path_handler_init()) {
		psd_err(0, "couldn't initialize path handler system");
		goto error_exit;
	}

	if (psd_route_init()) {
		psd_err(0, "couldn't initialize routing system");
		goto error_exit;
	}

	if (psd_authentication_init() != 0) {
		psd_err(0, "couldn't initialize authentication system");
		goto error_exit;
	}

	if (psd_event_init() != 0) {
		psd_err(0, "couldn't initialize event subsystem");
		goto error_exit;
	}

	if (psd_protocol_init() != 0) {
		psd_err(0, "couldn't initialize protocols system");
		goto error_exit;
	}

	if (psd_listener_handler_init() != 0) {
		psd_err(0, "couldn't initialize listener handlers");
		goto error_exit;
	}

	if (psd_optimizers_init() != 0) {
		psd_err(0, "couldn't initialize optimizer system");
		goto error_exit;
	}

#ifdef EMBED_UDT
        if (psd_proto_udt_init() != 0) {
                psd_err(0, "couldn't initialize UDT system");
                goto error_exit;
        }
#endif

	if (psd_load_modules() != 0) {
		psd_err(0, "couldn't load modules");
		goto error_exit;
	}

	if (psd_frontend_default_start() != 0) {
		psd_err(0, "couldn't start default frontend");
		goto error_exit;
	}

	pthread_exit(0);

error_exit:
	sleep(1);
	exit(-1);
}
